﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;

using agvcontroller.Common;
using agvcontroller.Log;
using agvcontroller.Control;

namespace agvcontroller.Process
{
    class CJobManager
    {
        const int VEHICLEJOBIDLENGTH = 3;
        const int PRIORIRY_DEFAULT = 99;
        const int PRIORIRY_MAX = int.MaxValue;

        Thread thrdJob;
        CJobDic JobDic = new CJobDic();

        CRouteManager RouteManager = CRouteManager.getInstance();
        CVehicleDic VehicleDic = CVehicleDic.getInstance();

        Logger Log;

        bool JobEnd=false;

        private static CJobManager uniqueInstance;

        public static CJobManager getInstance()
        {
            if (uniqueInstance == null)
            {
                uniqueInstance = new CJobManager();
            }
            return uniqueInstance;
        }

        public CJobManager()
        {
            thrdJob = new Thread(new ThreadStart(thrdJobProcess));

            Log = new Logger("JobManager");
        }

        public int StartJobProcess()
        {
            thrdJob.Start();

            Log.Info("Start Job Process");

            return CReturnCode.SUCCESS;
        }

        public int AbortJobProcess()
        {
            thrdJob.Abort();

            Log.Info("Abort Job Process");

            return CReturnCode.SUCCESS;
        }

        public void thrdJobProcess()
        {
            while (!JobEnd)
            {
                //Process for Idle Vehicle
                ProcessIdle();

                //is exist job??
                if (JobDic.Count == 0)
                {
                    Thread.Sleep(100);
                    continue;
                }

                //sort job && Select one job
                CJob selectJob = SelectOneJob();
                if (selectJob == null) continue;

                //Select Vehicle
                CVehicle assignedVehicle = RouteManager.SearchNearestVehicle(selectJob);
                if (assignedVehicle == null) continue;

                //MakeCmd
                CVehicleJob vehiclejob = MakeVehiclJob(selectJob, assignedVehicle);
                if (vehiclejob == null) continue;

                //SendVechcle
                SendVehicleJob(assignedVehicle, vehiclejob);

                Thread.Sleep(100);
            }
        }

        CJob SelectOneJob()
        {
            try
            {
                if (JobDic == null) return null;

                var sortedJob = from job in JobDic orderby job.Value.Priority, job.Value.QueuedTime descending select job;

                foreach (var job in sortedJob)
                {
                    if (job.Value.State > enumJobState.SENDJOB)
                        return job.Value;
                }

                return JobDic.Values.First<CJob>();
            }
            catch (Exception ex)
            {
                Log.Exception(string.Format("{0},{1}", ex.Message, ex.StackTrace));
                return null;
            }
        }

        CVehicleJob MakeVehiclJob(CJob job , CVehicle assignedVehicle)
        {
            List<CNode> route = RouteManager.SearchNearestRoute(job, assignedVehicle);

            if (route == null)
                return null;

            CVehicleJob vehiclejob = new CVehicleJob();

            vehiclejob.ID = CreateNewVehicleJobID(job);
            vehiclejob.Route = route;
            vehiclejob.Action = RouteManager.SearchAction(route, assignedVehicle);
            vehiclejob.Speed = RouteManager.SearchSpeed(route, assignedVehicle);
            vehiclejob.Direct = RouteManager.SearchDirect(route, assignedVehicle);

            job.VehicleJobList.Add(vehiclejob);

            return vehiclejob;
        }

        int SendVehicleJob(CVehicle vehicle, CVehicleJob vehiclejob)
        {
            vehicle.SetVehicleJob(vehiclejob);

            CJob job = GetJobbyVehicleJobID(vehiclejob.ID);
            job.State = enumJobState.SENDJOB;

            return CReturnCode.SUCCESS;
        }

        public int RequestJobFromTerminal(CTerminal terminal, int jobno)
        {
            CJob newjob = new CJob();

            newjob.ID = CreateNewJobID();
            newjob.Priority = PRIORIRY_DEFAULT;
            newjob.Source = terminal.TerminalNodeID;
            newjob.Dest = terminal.GetJobNodeID(jobno);
            newjob.Type = "Transfer";
            newjob.QueuedTime = DateTime.Now;
            newjob.State = enumJobState.QUEUED;

            JobDic.AddJob(newjob);

            Log.Info(string.Format("Job From Terminal {0} Added Job Queue",terminal));

            return CReturnCode.SUCCESS;
        }

        public int CalcenJobFromTerminal(CTerminal terminal, int jobno)
        {
            return CReturnCode.SUCCESS;
        }

        string CreateNewJobID()
        {
            return string.Format("{0}{1}{2}{3}{4}{5}{6}"
                ,DateTime.Now.Year,DateTime.Now.Month,DateTime.Now.Day,DateTime.Now.Hour
                ,DateTime.Now.Minute,DateTime.Now.Second,DateTime.Now.Millisecond);
        }

        string CreateNewVehicleJobID(CJob job)
        {
            return string.Format("{0}{1:000}", job.ID, job.VehicleJobList.Count + 1);
        }

        public CJob GetJobbyVehicleJobID(string vehiclejobid)
        {
            string jobID = vehiclejobid.PadRight(VEHICLEJOBIDLENGTH);

            return JobDic[jobID];
        }

        void ProcessIdle()
        {
            //Parking Area이동

            //Low Battery 처리
        }

        public int RequestManualJob(int sourceNodeID, int destNodeID, string jobType, string carrierID, int vehicleID)
        {
            CJob newjob = new CJob();

            newjob.ID = CreateNewJobID();
            newjob.Priority = PRIORIRY_MAX;
            newjob.Source = sourceNodeID;
            newjob.Dest = destNodeID;
            newjob.Type = jobType;
            newjob.CarrierID = carrierID;
            newjob.QueuedTime = DateTime.Now;
            newjob.State = enumJobState.QUEUED;

            JobDic.AddJob(newjob);

            Log.Info(string.Format("Job by Manual {0} Added Job Queue (Source : {1}, Dest : {2} , CarrierID{3}", vehicleID, sourceNodeID,destNodeID , carrierID));

            return CReturnCode.SUCCESS;
        }
    }
}
